using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web.UI.WebControls;
using System.Drawing;
using System.Web.UI;
using System.ComponentModel;
using Q.Framework.Utility;
using Q.Framework.Extension;

namespace Q.Framework.WebControls
{
    public enum TypeExpression : short
    {
        None,
        Digito,
        Decimal,
        DecimalNegativo,
        Email,
        Telefone,
        Cep,
        Data,
        Hora,
        DataHora,
        Inteiro,
        Cnpj,
        Cpf,
        CpfCnpj,
        CartaoCredito,
        Placa,
        Year,
        IntegerWithoutDot
    }

    public enum CharacterCasing
    {
        None,
        Uppercase,
        Lowercase
    }

    public class QTextBox : TextBox, IBindableControl
    {
        private CharacterCasing _case = WebControls.CharacterCasing.None;

        public CharacterCasing Case { get { return _case; } set { _case = value; } }

        public virtual TypeExpression ValueType { get; set; }

        public Boolean Required { get; set; }

        RegularExpressionValidator _validator = new RegularExpressionValidator();

        public String ValidationExpression
        {
            get { return _validator.ValidationExpression; }
            set { _validator.ValidationExpression = value; }
        }

        /// <summary>
        /// Mensagem de Validação
        /// </summary>
        public String ValidationMessage { get; set; }

        /// <summary>
        /// Altera ou retorna a forma de visualização da validação
        /// </summary>
        public ValidatorDisplay Display
        {
            get { return _validator.Display; }
            set { _validator.Display = value; }
        }


        /// <summary>
        /// Retorna se o valor digitado é vazio ou não
        /// </summary>
        /// <value>
        ///   <c>true</c> se for vazio; senão, <c>false</c>.
        /// </value>
        public Boolean IsEmpty
        {
            get { return String.IsNullOrEmpty(this.Text); }
        }

        private void CreateRegularExpressionValidator()
        {
            // fixa o controle que será validado
            _validator.ControlToValidate = this.ID;
            switch (ValueType)
            {
                case TypeExpression.None:
                    break;
                case TypeExpression.Digito:
                    break;
                case TypeExpression.Decimal:
                    break;
                case TypeExpression.DecimalNegativo:
                    break;
                case TypeExpression.Email:
                    break;
                case TypeExpression.Telefone:
                    break;
                case TypeExpression.Cep:
                    break;
                case TypeExpression.Data:
                    break;
                case TypeExpression.Hora:
                    _validator = new RegularExpressionValidator();
                    _validator.ID = String.Format("{0}_{1}", "ExpressionValidator", this.ID);
                    _validator.ControlToValidate = this.ID;
                    _validator.Text = String.Format(" {0}", ValidationMessage == null ? " <img style=\"border-bottom: 0px; border-left: 0px; border-top: 0px; border-right: 0px;\" src='/Imagens/Private.png' />" : ValidationMessage);
                    _validator.ToolTip = ValidationMessage == null ? "Campo Obrigatório" : String.Empty;
                    _validator.Display = ValidatorDisplay.Dynamic;
                    _validator.ForeColor = System.Drawing.Color.Red;
                    _validator.ValidationGroup = "validationGroup";
                    _validator.ValidationExpression = @"^(20|21|22|23|[01]\d|\d)(([:][0-5]\d){1,2})$";
                    break;
                case TypeExpression.DataHora:
                    break;
                case TypeExpression.Inteiro:
                    break;
                case TypeExpression.Cnpj:
                    break;
                case TypeExpression.Cpf:
                    break;
                case TypeExpression.CartaoCredito:
                    break;
                case TypeExpression.Placa:
                    break;
                case TypeExpression.Year:
                    break;
                case TypeExpression.IntegerWithoutDot:
                    break;
                default:
                    break;
            }
            if (!String.IsNullOrEmpty(this.ValidationExpression))
                Controls.Add(_validator);
        }

        protected override void AddAttributesToRender(HtmlTextWriter writer)
        {
            base.AddAttributesToRender(writer);
            if (this.TextMode == TextBoxMode.Password)
            {
                string text = this.Text;
                if (text.Length > 0)
                    writer.AddAttribute(HtmlTextWriterAttribute.Value, text);
            }
        }

        private CustomValidator _required;

        private void CreateRequiredFieldValidator()
        {
            if (this.Required)
            {
                _required = new CustomValidator();
                _required.ID = String.Format("{0}_{1}", "RequiredValidator", this.ID);
                _required.ControlToValidate = this.ID;
                String srcValidationImage = System.Web.Configuration.WebConfigurationManager.AppSettings["ImagemValidacao"] ?? "/Imagens/Private.png";
                _required.Text = String.Format(" {0}", ValidationMessage == null ? String.Format(" <img style=\"border-bottom: 0px; border-left: 0px; border-top: 0px; border-right: 0px;\" src='{0}' />", srcValidationImage) : ValidationMessage);
                _required.ToolTip = ValidationMessage == null ? "Campo Obrigatório" : String.Empty;
                _required.Display = ValidatorDisplay.Dynamic;
                _required.ForeColor = System.Drawing.Color.Red;
                _required.ValidationGroup = String.IsNullOrWhiteSpace(this.ValidationGroup) ? "validationGroup" : ValidationGroup;
                _required.ValidateEmptyText = true;
                _required.ClientValidationFunction = "ValidateTextBox";
                _required.ServerValidate += new ServerValidateEventHandler(_required_ServerValidate);
                Controls.Add(_required);
                this.Style.Add("border-left", "4px solid #B22222");
            }
            else
            {
                if (Controls.Contains(_required))
                    Controls.Remove(_required);
            }
        }

        void _required_ServerValidate(object source, ServerValidateEventArgs args)
        {
            bool isValid = Text != "";
            BackColor = isValid ? Color.White : Color.Red;
            args.IsValid = isValid;
        }

        /// <summary>
        /// Altera ou retorna se o valor digitado será completado com zero até o MaxLength
        /// </summary>
        /// <value>
        ///   <c>true</c> se for completar; senão, <c>false</c>.
        /// </value>
        public Boolean CompleteWithZero { get; set; }

        /// <summary>
        /// Sets input focus to a control.
        /// </summary>
        public override void Focus()
        {
            JavaScriptAdd.SetFocus(this.Page, this.ClientID);
        }

        /// <summary>
        /// Altera o valor do atributo alt. Atributo utilizado pelo MeioMask
        /// </summary>
        public void SetAltAttribute()
        {
            switch (ValueType)
            {
                case TypeExpression.CpfCnpj:
                    Attributes["OnBlur"] += ";AplicarMascaraCpfCnpj(this);";
                    Attributes["OnFocus"] += ";CpfCnpjRemoveMask(this);";
                    Attributes["OnKeyPress"] += ";return numbersonly(this, event)";
                    MaxLength = 14;
                    break;
                case TypeExpression.None:
                case TypeExpression.Digito:
                    break;
                case TypeExpression.Decimal:
                    this.Attributes.Add("alt", "decimal");
                    break;
                case TypeExpression.DecimalNegativo:
                    this.Attributes.Add("alt", "signed-decimal");
                    break;
                //case TypeExpression.DecimalJs:
                //    this.Attributes.Add("onkeyup", "formataValor(this,event);");
                //    break;
                case TypeExpression.Email:
                    break;
                case TypeExpression.Telefone:
                    this.Attributes.Add("alt", "phone");
                    break;
                case TypeExpression.Cep:
                    this.Attributes.Add("alt", "cep");
                    break;
                case TypeExpression.Data:
                    this.Attributes.Add("alt", "date");
                    this.MaxLength = 10;
                    break;
                case TypeExpression.Inteiro:
                    this.Attributes.Add("alt", "integer");
                    break;
                case TypeExpression.DataHora:
                    this.Attributes.Add("alt", "datetime");
                    break;
                case TypeExpression.Hora:
                    this.Attributes.Add("alt", "time");
                    break;
                case TypeExpression.Cnpj:
                    this.Attributes.Add("alt", "cnpj");
                    this.MaxLength = 18;
                    break;
                case TypeExpression.Cpf:
                    this.Attributes.Add("alt", "cpf");
                    this.MaxLength = 14;
                    break;
                case TypeExpression.CartaoCredito:
                    this.Attributes.Add("alt", "cc");
                    break;
                case TypeExpression.Placa:
                    this.Attributes.Add("alt", "placa");
                    break;
                case TypeExpression.Year:
                    this.Attributes.Add("alt", "year");
                    break;
                case TypeExpression.IntegerWithoutDot:
                    this.Attributes.Add("alt", "integerWithoutDot");
                    break;
                default:
                    break;
            }
        }

        /// <summary>
        /// Retorna ou altera o valor de acordo com o <see cref="ValueType">ValueType</see>
        /// </summary>
        public void SetValue(object value)
        {
            if (value != null)
            {
                switch (ValueType)
                {
                    case TypeExpression.Digito:
                    case TypeExpression.Inteiro:
                    case TypeExpression.IntegerWithoutDot:
                    case TypeExpression.Year:
                        Text = value.ToString();
                        break;
                    case TypeExpression.Decimal:
                    case TypeExpression.DecimalNegativo:
                        Text = (value as Decimal?).Value.Format();
                        break;
                    case TypeExpression.Data:
                    case TypeExpression.DataHora:
                        (value as DateTime?).Value.Format(ValueType == TypeExpression.Data ? FormatDateTime.Date : FormatDateTime.DateTime);
                        break;
                    default:
                        Text = value as String;
                        break;
                }
            }
        }

        /// <summary>
        /// Registers client script for generating postback events prior to rendering on the client, if <see cref="P:System.Web.UI.WebControls.TextBox.AutoPostBack"/> is true.
        /// </summary>
        /// <param name="e">An <see cref="T:System.EventArgs"/> that contains the event data.</param>
        protected override void OnPreRender(EventArgs e)
        {

            base.OnPreRender(e);

            if (!this.DesignMode)
            {
                CreateRegularExpressionValidator();
                CreateRequiredFieldValidator();
                this.CssClass = "txtBox";

                switch (Case)
                {
                    case CharacterCasing.Uppercase:
                        Style.Add("text-transform", "uppercase");
                        break;
                    case CharacterCasing.Lowercase:
                        Style.Add("text-transform", "lowercase");
                        break;
                    default:
                        Style.Remove("text-transform");
                        break;
                }

                if (CompleteWithZero)
                    this.Attributes.Add("OnBlur", String.Format("padL('{0}',{1}", ClientID, MaxLength));

                SetAltAttribute();

                CheckTextBox();
            }
        }

        /// <summary>
        /// Renders the specified w.
        /// </summary>
        /// <param name="w">The w.</param>
        protected override void Render(HtmlTextWriter w)
        {
            base.Render(w);

            if (!this.DesignMode)
            {
                if (this.Required)
                {
                    if (_required == null)
                    {
                        if (!String.IsNullOrWhiteSpace(this.Style["border-left"]))
                            this.Style.Remove("border-left");
                        CreateRequiredFieldValidator();
                    }
                    _required.RenderControl(w);
                }

                _validator.RenderControl(w);

                CheckTextBox();
            }
        }

        private void CheckTextBox()
        {
            if (this.Enabled)
            {
                this.CssClass = "txtBox";
                this.Enabled = true;
                this.ReadOnly = false;
                this.ForeColor = System.Drawing.Color.Black;
            }
            else
            {
                this.CssClass = "txtBoxEnabled";
                this.Enabled = false;
                this.ReadOnly = true;
                this.ForeColor = System.Drawing.Color.Black;
            }
        }

        /// <summary>
        /// Retorna se o Valor é válido baseado em um tipo de dados
        /// </summary>
        /// <typeparam name="T">Tipo de Dados(Decimal, String, Int32, etc...)</typeparam>
        /// <returns></returns>
        public Boolean IsValidValue<T>()
        {
            if (String.IsNullOrEmpty(this.Text))
                return false;

            Boolean result = false;

            if (typeof(T) == typeof(DateTime))
            {
                DateTime dateParse;
                result = DateTime.TryParse(this.Text, out dateParse);
            }
            else if (typeof(T) == typeof(Int32))
            {
                Int32 valorParse;
                result = Int32.TryParse(this.Text, out valorParse);
            }
            else if (typeof(T) == typeof(Decimal))
            {
                Decimal valorParse;
                result = Decimal.TryParse(this.Text, out valorParse);
            }
            else
                throw new ArgumentException("Tipo não pode ser validado");

            return result;
        }

        /// <summary>
        /// Retorna o texto
        /// </summary>
        public override string Text
        {
            get
            {
                String retorno = base.Text.Trim();
                if (ValueType == TypeExpression.Inteiro || ValueType == TypeExpression.CpfCnpj || ValueType == TypeExpression.Cpf || ValueType == TypeExpression.Cnpj)
                    retorno = retorno.RemoveMask();
                switch (Case)
                {
                    case CharacterCasing.Uppercase:
                        retorno = retorno.ToUpper(); break;
                    case CharacterCasing.Lowercase:
                        retorno = retorno.ToLower(); break;
                }

                return retorno;
            }
            set
            {
                if (ValueType == TypeExpression.CpfCnpj || ValueType == TypeExpression.Cpf || ValueType == TypeExpression.Cnpj)
                {
                    value = value.RemoveMask();
                    switch (ValueType)
                    {
                        case TypeExpression.Cnpj: value = value.Mask(StringExtension.TypeMask.Cnpj);
                            break;
                        case TypeExpression.Cpf: value = value.Mask(StringExtension.TypeMask.Cpf);
                            break;
                        case TypeExpression.CpfCnpj: value = value.Mask(StringExtension.TypeMask.CpfOrCnpj);
                            break;
                    }
                }
                base.Text = value;
            }
        }

        private Boolean TryParse<T>(out T result) where T : IConvertible
        {
            try
            {
                result = (T)System.Convert.ChangeType(this.Text, typeof(T));
                return true;
            }
            catch (OverflowException) { throw; }
            catch (Exception)
            {
                result = default(T);
                return false;
            }
        }

        /// <summary>
        /// Converte o text digitado em um determinado tipo de Dados
        /// </summary>
        /// <typeparam name="T">Tipo de Dados</typeparam>
        /// <returns></returns>
        public T As<T>() where T : IConvertible, new()
        {
            T t = new T();
            TryParse(out t);

            return t;
        }

        public Nullable<T> AsNullable<T>() where T : struct
        {
            if (!string.IsNullOrWhiteSpace(Text))
            {
                TypeConverter conv = TypeDescriptor.GetConverter(typeof(Nullable<>).MakeGenericType(typeof(T)));
                return (Nullable<T>)conv.ConvertFrom(Text);
            }
            return null;
        }

        public Decimal ToDecimal()
        {
            return String.IsNullOrEmpty(this.Text) ? 0 : IsValidValue<Decimal>() ? Decimal.Parse(this.Text) : 0;
        }

        /// <summary>
        /// Converte o texto e retorna o tipo Int32
        /// </summary>
        /// <returns></returns>
        public Int32 ToInt32()
        {
            return String.IsNullOrEmpty(this.Text) ? 0 : IsValidValue<Int32>() ? Int32.Parse(this.Text) : 0;
        }

        /// <summary>
        /// Converte o texto e retorna o tipo DateTime
        /// </summary>
        /// <returns></returns>
        public DateTime ToDateTime()
        {
            return String.IsNullOrEmpty(this.Text) ? DateTime.MinValue : IsValidValue<DateTime>() ? DateTime.Parse(this.Text) : DateTime.MinValue;
        }


        public string DataPropertyName { get; set; }

        private String controlBindableProperty = "Text";
        public string ControlBindableProperty
        {
            get { return controlBindableProperty; }
            set { controlBindableProperty = value; }
        }
    }
}